"""
自动故障恢复服务
实现系统自动故障检测和恢复机制
"""

import asyncio
import time
from datetime import datetime, timedelta
from typing import Dict, Any, Callable, List
from src.utils.logging import get_logger
from src.services.monitoring_service import monitoring_service

logger = get_logger(__name__)


class AutoRecoveryService:
    """自动故障恢复服务类"""
    
    def __init__(self):
        self.recovery_rules: Dict[str, Dict[str, Any]] = {}
        self.recovery_actions: Dict[str, Callable] = {}
        self.recovery_history: List[Dict[str, Any]] = []
        self.monitoring_enabled = True
        self.check_interval = 60  # 默认检查间隔60秒
        self._running = False
        logger.info("自动故障恢复服务初始化完成")
    
    def add_recovery_rule(self, rule_name: str, condition: Callable[[], bool], 
                         action: Callable, threshold: int = 3, window: int = 300):
        """
        添加恢复规则
        
        Args:
            rule_name: 规则名称
            condition: 条件检查函数
            action: 恢复动作函数
            threshold: 触发阈值（在时间窗口内满足条件的次数）
            window: 时间窗口（秒）
        """
        self.recovery_rules[rule_name] = {
            "condition": condition,
            "action": action,
            "threshold": threshold,
            "window": window,
            "trigger_count": 0,
            "last_triggers": []
        }
        logger.info(f"已添加恢复规则: {rule_name}")
    
    def register_recovery_action(self, action_name: str, action: Callable):
        """
        注册恢复动作
        
        Args:
            action_name: 动作名称
            action: 动作函数
        """
        self.recovery_actions[action_name] = action
        logger.info(f"已注册恢复动作: {action_name}")
    
    async def start_monitoring(self):
        """启动监控"""
        if self._running:
            logger.warning("自动故障恢复监控已在运行")
            return
            
        self._running = True
        logger.info("启动自动故障恢复监控")
        
        while self._running:
            try:
                await self._check_recovery_conditions()
                await asyncio.sleep(self.check_interval)
            except Exception as e:
                logger.error(f"自动故障恢复检查出错: {e}")
    
    def stop_monitoring(self):
        """停止监控"""
        self._running = False
        logger.info("停止自动故障恢复监控")
    
    async def _check_recovery_conditions(self):
        """检查恢复条件"""
        current_time = datetime.now()
        
        for rule_name, rule in self.recovery_rules.items():
            try:
                # 检查条件
                condition_met = rule["condition"]()
                
                if condition_met:
                    # 记录触发时间
                    rule["last_triggers"].append(current_time)
                    
                    # 清除过期的触发记录
                    expiry_time = current_time - timedelta(seconds=rule["window"])
                    rule["last_triggers"] = [
                        t for t in rule["last_triggers"] if t > expiry_time
                    ]
                    
                    # 检查是否达到阈值
                    if len(rule["last_triggers"]) >= rule["threshold"]:
                        logger.warning(f"恢复规则 {rule_name} 达到触发阈值，执行恢复动作")
                        await self._execute_recovery_action(rule_name, rule)
                        # 重置触发计数
                        rule["last_triggers"].clear()
                else:
                    # 条件未满足，清除旧的触发记录
                    expiry_time = current_time - timedelta(seconds=rule["window"])
                    rule["last_triggers"] = [
                        t for t in rule["last_triggers"] if t > expiry_time
                    ]
                    
            except Exception as e:
                logger.error(f"检查恢复规则 {rule_name} 时出错: {e}")
    
    async def _execute_recovery_action(self, rule_name: str, rule: Dict[str, Any]):
        """执行恢复动作"""
        # 初始化 recovery_entry
        recovery_entry = {
            "rule_name": rule_name,
            "timestamp": datetime.now(),
            "status": "executing"
        }
        
        try:
            # 添加到恢复历史
            self.recovery_history.append(recovery_entry)
            
            # 执行恢复动作
            result = await rule["action"]()
            
            # 更新恢复历史
            recovery_entry["status"] = "completed"
            recovery_entry["result"] = result
            recovery_entry["completed_at"] = datetime.now()
            
            logger.info(f"恢复动作 {rule_name} 执行完成，结果: {result}")
            
        except Exception as e:
            recovery_entry["status"] = "failed"
            recovery_entry["error"] = str(e)
            recovery_entry["completed_at"] = datetime.now()
            logger.error(f"执行恢复动作 {rule_name} 失败: {e}")
    
    def get_recovery_history(self, limit: int = 50) -> List[Dict[str, Any]]:
        """
        获取恢复历史
        
        Args:
            limit: 返回记录数限制
            
        Returns:
            恢复历史记录
        """
        return self.recovery_history[-limit:] if self.recovery_history else []
    
    def get_active_rules(self) -> List[str]:
        """
        获取活跃规则列表
        
        Returns:
            活跃规则列表
        """
        return list(self.recovery_rules.keys())


# 全局自动恢复服务实例
auto_recovery_service = AutoRecoveryService()


# 示例恢复动作函数
async def restart_service_action():
    """重启服务动作示例"""
    logger.info("执行服务重启动作")
    # 这里可以实现实际的服务重启逻辑
    await asyncio.sleep(1)  # 模拟操作耗时
    return {"status": "success", "message": "服务重启完成"}


async def clear_cache_action():
    """清理缓存动作示例"""
    logger.info("执行缓存清理动作")
    # 这里可以实现实际的缓存清理逻辑
    monitoring_service.update_cache_size(0)
    return {"status": "success", "message": "缓存清理完成"}


async def reconnect_db_action():
    """数据库重连动作示例"""
    logger.info("执行数据库重连动作")
    # 这里可以实现实际的数据库重连逻辑
    monitoring_service.update_db_connections(0)
    await asyncio.sleep(1)  # 模拟操作耗时
    return {"status": "success", "message": "数据库重连完成"}


# 注册示例恢复动作
auto_recovery_service.register_recovery_action("restart_service", restart_service_action)
auto_recovery_service.register_recovery_action("clear_cache", clear_cache_action)
auto_recovery_service.register_recovery_action("reconnect_db", reconnect_db_action)